
/****************************************************************************/
/*Copyright (c) 2011, Florent DEVILLE.                                      */
/*All rights reserved.                                                      */
/*                                                                          */
/*Redistribution and use in source and binary forms, with or without        */
/*modification, are permitted provided that the following conditions        */
/*are met:                                                                  */
/*                                                                          */
/* - Redistributions of source code must retain the above copyright         */
/*notice, this list of conditions and the following disclaimer.             */
/* - Redistributions in binary form must reproduce the above                */
/*copyright notice, this list of conditions and the following               */
/*disclaimer in the documentation and/or other materials provided           */
/*with the distribution.                                                    */
/* - The names of its contributors cannot be used to endorse or promote     */
/*products derived from this software without specific prior written        */
/*permission.                                                               */
/* - The source code cannot be used for commercial purposes without         */ 
/*its contributors' permission.                                             */
/*                                                                          */
/*THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS       */
/*"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT         */
/*LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS         */
/*FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE            */
/*COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,       */
/*INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,      */
/*BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;          */
/*LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER          */
/*CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT        */
/*LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN         */
/*ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE           */
/*POSSIBILITY OF SUCH DAMAGE.                                               */
/****************************************************************************/

#include "GRTSpotLight.h"

#include "GRTGLVisu.h"
//#include <QGLWidget>
#include <QtOpenGL\qgl.h>
#include "RTMatrix44.h"
#include "GRTScene.h"

#include "GRTSpotAt.h"
#include "GRTMaterial.h"
#include "GRTMemoryLeak.h"
//constructor
GRTSpotLight::GRTSpotLight(GRTSpotAt* atElement) : GRTILight(LT_SPOT), _attenuation(1, 0, 0), 
	_cutOff(45), _exponent(0), _atElement(atElement)
{
	_translateMark = RTVector3f(0, 1.5, 0);
	_rotationMarkRadius = 2.f;
	if(atElement != 0)
	{
		_direction = _atElement->getTranslate() - _translate;
		_direction.normalize();

		//set the at element
		_atElement->setGRTSpotLightElement(this);
	}
}

//destructor
GRTSpotLight::~GRTSpotLight()
{

}

//pre render the object
void GRTSpotLight::preRender()
{
	//calculate world matrix
	_direction = _atElement->getTranslate() - _translate;
	_direction.normalize();
	
	RTVector3f up(0, 1, 0);
	up.normalize();
	
	RTMatrix44 m;
	m.createFrenet(_translate, _direction, up);

	m.getOpenGLMatrix(_frenet);

	glPushMatrix();

	glMultMatrixd(_frenet);
}

//render the object
void GRTSpotLight::Render()
{
	VISU.drawGRTSpotLight();
}

//post render
void GRTSpotLight::postRender()
{
	//if the sphere is selected, draw the surrounding box and the translation mark
	if(_selected)
	{
		glPushMatrix();
		VISU.drawSelectionBox(2, 2, 3);
		glPopMatrix();
	}

	glPopMatrix();

	//draw the line between the light and its at point
	glDisable(GL_LIGHTING);
	glLineWidth(1);
	glColor3f(1, 1, 1);
	glBegin(GL_LINES);
		glVertex3f(_translate.x, _translate.y, _translate.z);
		glVertex3f(_atElement->getTranslate().x, _atElement->getTranslate().y, _atElement->getTranslate().z);
	glEnd();
	glEnable(GL_LIGHTING);

	//add the spot light to the opengl scene
	int lightID = SCENE.getLightID();
	VISU.addGRTSpotLight(lightID, _translate, _direction, _attenuation, _cutOff, _exponent, _material);
	lightID++;
	SCENE.setLightID(lightID);
}

//determine if the point v is a point of the geometry object
bool GRTSpotLight::isGRTGeometry(const RTVector3f& v) const
{
	//calculate world matrix
	RTVector3f up(0, 1, 0);
	up.normalize();
	
	RTMatrix44 transform;
	transform.createFrenet(_translate, _direction, up);

	//get V in local coordinate of the sphere
	RTVector3f localV = v * transform.inverse();

	//check the heigt
	const float offsetError = 0.2f;
	const float radius = 1.f;
	const float height = 1.5f;

	if(localV.z > offsetError || localV.z < -height - offsetError)
		return false;

	//calculate the radius at the specific height
	float localRadius = radius - (localV.z / height) * radius;

	localV.z = 0;
	if(localV.norme() > localRadius + offsetError)
		return false;

	return true;
}

//test if the point v is on the mark of the primitive
//out indicate which axis is hoovered
bool GRTSpotLight::isTranslationMark(const RTVector3f& v)
{
	//calculate the world matrix of the primitive
	//calculate world matrix
	//_direction = _atElement->getTranslate() - _translate;
	//_direction.normalize();
	
	RTVector3f up(0, 1, 0);
	up.normalize();
	
	RTMatrix44 transform;
	transform.createFrenet(_translate, _direction, up);

	RTMatrix44 t;
	t.createTranslation(_translateMark);
	transform = t * transform;

	_hoover.x = 0;
	_hoover.y = 0;
	_hoover.z = 0;

	//get the point in the local coordinates of the translation mark
	RTVector3f distance = v * transform.inverse();

	//get and test the distance between the point and the origin of the translation mark
	//RTVector3f distance = v - center;
	if(distance.norme2() > 1)
		return false;

	distance.normalize();

	const float treshold = 0.8f;

	//text with the tree axis.
	RTVector3f x(1, 0, 0);
	if(x.dot(distance) > treshold)
	{
		_hoover.x = 1;
		return true;
	}

	RTVector3f y(0, 1, 0);

	if(y.dot(distance) > treshold)
	{
		_hoover.y = 1;
		return true;
	}
	RTVector3f z(0, 0, 1);
	if(z.dot(distance) > treshold)
	{
		_hoover.z = 1;
		return true;
	}

	//v is not the mark
	return false;
}

//attenuation gettors
RTVector3f GRTSpotLight::getAttenuation()const
{
	return _attenuation;
}

//cut off angle gettors
float GRTSpotLight::getCutOff()const
{
	return _cutOff;
}

//distribution exponent light
float GRTSpotLight::getExponent()const
{
	return _exponent;
}

//attenuation setter
void GRTSpotLight::setAttenuation(const RTVector3f& attenuation)
{
	_attenuation = attenuation;
}

//set the cut off angle
void GRTSpotLight::setCutOff(float angle)
{
	_cutOff = angle;
}

//get the distrubition exponent light
void GRTSpotLight::setExponent(float e)
{
	_exponent = e;
}

//get the rotation matrix of the movable object
RTMatrix44 GRTSpotLight::getRotationMatrix()const
{
	//calculate the rotation matrix of the primitive
	RTVector3f up(0, 1, 0);
	up.normalize();
	
	RTMatrix44 transform;
	transform.createFrenet(RTVector3f(0, 0, 0), _direction, up);

	return transform;
}

//get the at element of the light
GRTSpotAt* GRTSpotLight::getAtElement()
{
	return _atElement;
}

//set the at element
void GRTSpotLight::setAtElement(GRTSpotAt* at)
{
	_atElement = at;
}

//draw as in render
void GRTSpotLight::drawAsRender()
{
	//add the spot light to the opengl scene
	int lightID = SCENE.getLightID();
	VISU.addGRTSpotLight(lightID, _translate, _direction, _attenuation, _cutOff, _exponent, _material);
	lightID++;
	SCENE.setLightID(lightID);
}

//calculate and return the direction
RTVector3f GRTSpotLight::calculateDirection()
{
	_direction = _atElement->getTranslate() - _translate;
	_direction.normalize();
	return _direction;
}